/*
 * Copyright (C) 2012, 2013 by Ralph Germ, Martin Budden, Arne Kesting, Martin Treiber
 * <ralph.germ@gmail.com>
 * Copyright (C) 2013 by Osama Abdelkhaleq, Andrew Foxworth, Jasmine Griffiths, Kyle Kenik
 * -----------------------------------------------------------------------------------------
 * 
 * This file is part of
 * 
 * MovSimDroid.
 * 
 * MovSimDroid is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MovSimDroid is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MovSim. If not, see <http://www.gnu.org/licenses/>
 * or <http://www.movsim.org>.
 * 
 * -----------------------------------------------------------------------------------------
 * 
 *
 * * @file MovSimDroidActivity.java
 * @author Gwen Nellen and Raul Barrera, (c) July 15, 2013
 * @description MovSimDroid handles level presentation and control,
 *              gameplay, and results transmission/storage
 * @note Contains custom SendScoreAsyncTask for asynchronously sending local
 *      results from user device to remote database     
 * -----------------------------------------------------------------------------------------     
 */
package org.movsim.movdroid;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.log4j.Level;
import org.movsim.input.ProjectMetaData;
import org.movsim.movdroid.graphics.MovSimTrafficView;
import org.movsim.movdroid.util.FormatUtil;
import org.movsim.movdroid.util.HighscoreEntry;
import org.movsim.movdroid.util.OnFirstBoot;
import org.movsim.simulator.SimulationRun;
import org.movsim.simulator.SimulationRunnable;
import org.movsim.simulator.Simulator;
import org.movsim.simulator.roadnetwork.RoadNetwork;
import org.movsim.simulator.roadnetwork.VariableMessageSignBase;
import org.movsim.simulator.roadnetwork.VariableMessageSignDiversion;
import org.movsim.utilities.Units;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.view.Window;

//import com.actionbarsherlock.app.ActionBar.OnNavigationListener;

import org.movsim.movdroid.DatabaseHandler;

import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;

import de.mindpipe.android.logging.log4j.LogConfigurator;

/*
 * Description: Called by splash screen to set up all the data required for the
 * 		simulations.
 * 
 * Known Bugs: Pushing Action Start button to start the simulation in API 11 without
 * 		configuring lights results in a system crash.
 */
public class MovSimDroidActivity extends Activity implements
		SimulationRun.CompletionCallback,
		SimulationRunnable.UpdateStatusCallback {

	private Context context = this;
	int red = 20;
	int yellow = 10;
	int green = 20;
	int iPickedaLevel;
	String initials = "";
	// MovSim core uses slf4j as a logging facade for log4j.
	static {
		final LogConfigurator logConfigurator = new LogConfigurator();

		logConfigurator.setUseFileAppender(false);
		logConfigurator.setUseLogCatAppender(true);
		// logConfigurator.setFileName(Environment.getExternalStorageDirectory()
		// + "myapp.log");
		logConfigurator.setRootLevel(Level.INFO);
		// Set log level of a specific logger
		logConfigurator.setLevel("org.apache", Level.INFO);
		logConfigurator.configure();
	}

	private ProjectMetaData projectMetaData;
	private Simulator simulator;
	private SimulationRunnable simulationRunnable;

	private VariableMessageSignBase variableMessageSign = new VariableMessageSignDiversion();
	private MovSimTrafficView trafficView;
	private Menu menu;
	private RoadNetwork roadNetwork;
	private Resources res;
	private String projectName;
	private String projectPath;
	private MovSimActionBar movsimActionBar;
	private int navigationItemPosition;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {

		initials = getIntent().getExtras().getString("initials");
		//requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
		getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
		try {
			super.onCreate(savedInstanceState);
			res = getResources();

			// Replace parser from MovSim. -> Default values from DTD are not
			// set. -> update xml files from MovSim before!
			System.setProperty("org.xml.sax.driver",
					"org.xmlpull.v1.sax2.Driver");

			setupSimulator();

			//create action bar with spinner for selecting levels
			movsimActionBar = new MovSimActionBar(this, simulator);
			ArrayAdapter<CharSequence> adapter = ArrayAdapter
					.createFromResource(this, R.array.project,
							android.R.layout.simple_spinner_item);
			adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
			getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

			// Used to set traffic light timing to initial values
			Bundle extra = getIntent().getExtras();
			if (extra != null) {
				if (extra.containsKey("config")) {
					extra = extra.getBundle("config");
					int scen = extra.getInt("choice_Scen");
					red = (scen == 0) ? extra.getInt("red_light") : -1;
					yellow = (scen == 0) ? extra.getInt("yellow_light") : -1;
					green = (scen == 0) ? extra.getInt("green_light") : -1;
					System.out.println("red = " + red + " : yellow = " + yellow
							+ " : green = " + green);
				}
			}

			//assign listener for spinner components
			OnNavigationListener listn = new OnNavigationListener() {
				@Override
				public boolean onNavigationItemSelected(int itemPosition,
						long itemId) {
					// TODO Auto-generated method stub

					switch (itemPosition) {
					case 0:
						// level 1
						projectName = res.getStringArray(R.array.projectName)[0];
						projectPath = res.getStringArray(R.array.projectPath)[0];
						createInputStreams();
						simulator.loadScenarioFromXml(projectName, projectPath);
						simulationRunnable.start();
						simulationRunnable.pause();
						trafficView.resetGraphicproperties();
						trafficView.forceRepaintBackground();
						movsimActionBar.reset();
						return true;
					case 1:
						// level2
						projectName = res.getStringArray(R.array.projectName)[1];
						projectPath = res.getStringArray(R.array.projectPath)[1];
						createInputStreams();
						simulator.loadScenarioFromXml(projectName, projectPath);
						simulationRunnable.start();
						simulationRunnable.pause();
						trafficView.resetGraphicproperties();
						trafficView.forceRepaintBackground();
						movsimActionBar.reset();
						return true;
					case 2:
						// level 3
						projectName = res.getStringArray(R.array.projectName)[2];
						projectPath = res.getStringArray(R.array.projectPath)[2];
						createInputStreams();
						simulator.loadScenarioFromXml(projectName, projectPath);
						simulationRunnable.start();
						simulationRunnable.pause();
						trafficView.resetGraphicproperties();
						trafficView.forceRepaintBackground();
						movsimActionBar.reset();
						return true;
					default:
						return false;
					}

				}
			};

			// initializes the streets and such
			trafficView = new MovSimTrafficView(this, simulator,
					projectMetaData);

			setContentView(trafficView);
			if (simulationRunnable != null) {
				simulationRunnable.pause();
			}

			getActionBar().setListNavigationCallbacks(adapter, listn);

		} catch (Exception E) {
			Toast toast1 = Toast.makeText(getApplicationContext(),
					E.toString(), Toast.LENGTH_SHORT);
			toast1.show();
		}
	}

	/* Instantiate values for starting the simulation */
	private void setupSimulator() {
		projectMetaData = ProjectMetaData.getInstance();

		projectMetaData.setParseFromInputstream(true);
		projectMetaData.setInstantaneousFileOutput(false);

		simulator = new Simulator(projectMetaData);

		simulationRunnable = simulator.getSimulationRunnable();
		simulationRunnable.setCompletionCallback(this);
		simulationRunnable.addUpdateStatusCallback(this);

		roadNetwork = simulator.getRoadNetwork();
	}

	/* Assign menu text to menu items and give them appearance traits */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		this.menu = menu;
		menu.clear();
		menu.add(res.getString(R.string.start))
				.setIcon(R.drawable.ic_action_start)
				.setShowAsAction(
						MenuItem.SHOW_AS_ACTION_IF_ROOM
								| MenuItem.SHOW_AS_ACTION_WITH_TEXT);
		menu.add(res.getString(R.string.restart))
				.setIcon(R.drawable.ic_action_restart)
				.setShowAsAction(
						MenuItem.SHOW_AS_ACTION_IF_ROOM
								| MenuItem.SHOW_AS_ACTION_WITH_TEXT);

		menu.add(res.getString(R.string.action))
				.setIcon(R.drawable.ic_action_trafficlight)
				.setShowAsAction(
						MenuItem.SHOW_AS_ACTION_IF_ROOM
								| MenuItem.SHOW_AS_ACTION_WITH_TEXT);

		SubMenu subMenu1 = menu.addSubMenu(R.string.menu);
		// subMenu1.add(res.getString(R.string.faster));
		// subMenu1.add(res.getString(R.string.slower));
		subMenu1.add(res.getString(R.string.info));
		subMenu1.add(res.getString(R.string.movsimInfo));

		MenuItem subMenu1Item = subMenu1.getItem();
		subMenu1Item.setIcon(R.drawable.abs__ic_menu_moreoverflow_holo_dark);
		subMenu1Item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS
				| MenuItem.SHOW_AS_ACTION_WITH_TEXT);

		return super.onCreateOptionsMenu(menu);
	}

	/* Give selected item to Action Bar class to handle its event */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		movsimActionBar.selectAction(item);
		// Toast toast1 = Toast.makeText(getApplicationContext(),
		// "option "+item.toString(), Toast.LENGTH_SHORT);
		// toast1.show();
		return true;
	}

	/* Actions for Navigation items */
	public boolean onNavigationItemSelected(int itemPosition, long itemId) {
		this.navigationItemPosition = itemPosition;
		if (simulationRunnable != null) {
			simulationRunnable.pause();
		}
		OnFirstBoot.show(this, "start", itemPosition + "start.accepted",
				res.getStringArray(R.array.infoScenario)[itemPosition],
				res.getString(R.string.onFirstBoot_title));
		// project selection
		projectName = res.getStringArray(R.array.projectName)[itemPosition];
		projectPath = res.getStringArray(R.array.projectPath)[itemPosition];
		createInputStreams();
		simulator.loadScenarioFromXml(projectName, projectPath);
		simulationRunnable.start();
		simulationRunnable.pause();
		trafficView.resetGraphicproperties();
		trafficView.forceRepaintBackground();
		if (menu != null) {
			menu.getItem(0).setIcon(R.drawable.ic_action_start)
					.setTitle(R.string.start);

			// Set visibility of interaction button
			if (itemPosition == 0 || itemPosition == 1) {
				menu.getItem(2).setVisible(true);
			} else {
				menu.getItem(2).setVisible(false);
			}

		}
		return true;
	}

	/* Input streams from layout and data resources for simulations. */
	void createInputStreams() {
		try {
			String full = projectPath + projectName;
			InputStream movsimXml = getAssets().open(full + ".xml");
			projectMetaData.setMovsimXml(movsimXml);
			InputStream is = getAssets().open(full + ".xodr");
			projectMetaData.setNetworkXml(is);
			InputStream isProp = getAssets().open(full + ".properties");
			projectMetaData.setProjectProperties(isProp);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/* Change image for touch area of pause/play button when paused or playing */
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		onCreateOptionsMenu(menu);

		if (!simulationRunnable.isPaused()) {
			MenuItem item = menu.getItem(0);
			item.setIcon(R.drawable.ic_action_pause);
			item.setTitle(R.string.pause);
		}
	}

	/* Gather resulting data from simulation. */
	@Override
	public void simulationComplete(final double simulationTime) {
		final double totalVehicleTravelTime = roadNetwork
				.totalVehicleTravelTime();
		final double totalVehicleTravelDistance = roadNetwork
				.totalVehicleTravelDistance() * Units.M_TO_KM;
		final double totalVehicleFuelUsedLiters = roadNetwork
				.totalVehicleFuelUsedLiters();
		final String formatedSimulationDuration = FormatUtil
				.getFormatedTime(simulationTime);

		//feed simulation data to database
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				SimulationFinished finished = new SimulationFinished(res,
						totalVehicleTravelTime, totalVehicleTravelDistance,
						totalVehicleFuelUsedLiters, formatedSimulationDuration,
						simulationTime, MovSimDroidActivity.this, initials);
				Score score = new Score(0, simulationTime, red, yellow, green,
						initials);

				DatabaseHandler db = new DatabaseHandler(context);
				db.getWritableDatabase();
				System.out
						.println("------------------ \n added score!! \n ---------");
				db.addScore(score);
				System.out
						.println("------------------ \n close db!! \n ---------");
				db.close();
				if (NetworkUtil.isNetworkAvailable(context)) { // if network
																// connection is
																// available
					doSendResults(); // call transmit handler
					System.out
							.println("------------------ \n it sent the results!! \n ---------");
				} else { // if network connection unavailable
					// doNotSendResults(); // call unable-to-transmit handler
					// do nothing for now... fix this later
				}
			}
		});
	}

	@Override
	public void updateStatus(double simulationTime) {
		if (simulator.isFinished()) {
			// hack to simulationComplete
			simulationRunnable.setDuration(simulationTime);
		}
	}

	/* pause simulation and change icon. */
	@Override
	protected void onPause() {
		simulationRunnable.pause();
		menu.getItem(0).setIcon(R.drawable.ic_action_start)
				.setTitle(R.string.start);
		super.onPause();
	}

	/* Show movsim info dialog */
	public void showInfo(String info) {
		Intent intent = new Intent();
		intent.putExtra("message", info);
		intent.setClass(MovSimDroidActivity.this, InfoDialog.class);
		startActivity(intent);
	}

	public VariableMessageSignBase getVariableMessageSign() {
		return variableMessageSign;
	}

	public MovSimTrafficView getMovSimTrafficView() {
		return trafficView;
	}

	// starts the light configuration activity
	public void startConfig() {

		if (projectName.equals("level3")) {
			Intent config = new Intent(MovSimDroidActivity.this,
					TrafficLightConfigOneSet.class);
			config.putExtra("red", red);
			config.putExtra("yellow", yellow);
			config.putExtra("green", green);
			startActivityForResult(config, 0);
		} else {
			Intent config = new Intent(MovSimDroidActivity.this,
					TrafficLightConfig.class);
			config.putExtra("red", red);
			config.putExtra("yellow", yellow);
			config.putExtra("green", green);
			startActivityForResult(config, 0);
		}
	}

	// retrieves data from light configuration activity
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode == RESULT_OK && requestCode == 0) {
			System.out.println("meow meow meow");
			red = data.getExtras().getInt("red");
			yellow = data.getExtras().getInt("yellow");
			green = data.getExtras().getInt("green");
			trafficView.setLights(red, yellow, green);
		} else {

			System.out.println("MEOWMEOWMEOW");
		}
	}

	public Menu getMenu() {
		return menu;
	}

	public int getNavigationItemPosition() {
		return navigationItemPosition;
	}

	// handles transmission attempt event
	private void doSendResults() {
		// setMessage("Uploading Score..."); // set upload display message
		SendScoreAsyncTask task = new SendScoreAsyncTask(); // create new
															// SendScoreAsyncTask
															// to transmit
															// results
															// asynchronously
		task.execute(doSetParams()); // get results to send and execute
										// asynchronous task
	}

	// handles transmission of local results to remote database asynchronously
	private class SendScoreAsyncTask extends
			AsyncTask<ArrayList<NameValuePair>, Void, String> {

		@Override
		// task to do asynchronously
		protected String doInBackground(ArrayList<NameValuePair>... params) {
			System.out
					.println("------------------ \n async doinbackground!! \n ---------");
			String url = NetworkUtil.SEND_URL; // URL of remote script to store
												// local results remotely
			System.out.println("------------------ \n zURL!! " + url
					+ "\n ---------");
			String response = ""; // initialize String to hold response
			HttpParams httpParams = new BasicHttpParams(); // create Object to
															// hold Http
															// parameters
			int timeoutConnection = 5000; // specify connection timeout in
											// milliseconds
			int timeoutSocket = 5000; // specify socket timeout in milliseconds
			HttpConnectionParams.setConnectionTimeout(httpParams,
					timeoutConnection); // set connection timeout as specified
			HttpConnectionParams.setSoTimeout(httpParams, timeoutSocket); // set
																			// socket
																			// timeout
																			// as
																			// specified
			DefaultHttpClient client = new DefaultHttpClient(httpParams); // create
																			// HttpClient
																			// with
																			// desired
																			// Http
																			// parameters
																			// (timeouts)
			HttpPost httpPost = new HttpPost(url); // use POST method to
													// specified URL
			try { // try
				System.out
						.println("------------------ \n doinbackground try.catch!! \n ---------");
				System.out.println("------------------ \n params:" + params[0]
						+ " \n ---------");
				httpPost.setEntity(new UrlEncodedFormEntity(params[0])); // put
																			// local
																			// results
																			// in
																			// POST
																			// request
				HttpResponse execute = client.execute(httpPost); // execute POST
																	// request
																	// from
																	// client
				InputStream content = execute.getEntity().getContent(); // create
																		// InputStream
																		// from
																		// content
				BufferedReader buffer = new BufferedReader(
						new InputStreamReader(content)); // create
															// BufferedReader
															// from InputStream
				String s = ""; // String to hold response line
				while ((s = buffer.readLine()) != null) { // while line exists
					response += s; // append line to response
				}
				System.out
						.println("------------------ \n trycatch done!! \n ---------");
			} catch (Exception e) { // fail
				e.printStackTrace(); // print stack trace for debugging
				return null; // return null response
			}
			System.out.println("------------------ \n Response" + response
					+ "!! \n ---------");

			return response; // return response on success

		}

		protected void onPostExecute(String result) {
			if (result == null) { // if execution failed
				// didNotSendScore(); // call fail handler
				// fix this to do something when score is not sent...
			}

			int response = 0; // initialize response to 0
			try {
				response = Integer.parseInt(result); // attempt to parse result
														// as integer
			} catch (NumberFormatException e) {
			}
			System.out.println("response:::" + response);
			if (response != 7)
				;// fix to do something...
			// didNotSendScore(); // if execution failed, call fail handler

			else
				didSendScore(); // if execution successful, call success handler
		}
	}

	private void didSendScore() {
		DatabaseHandler db = new DatabaseHandler(this); // create new
														// DatabaseHandler
														// object
		db.deleteScores(); // clear all rows from yet-to-submit table
		db.close(); // close DatabaseHandler
		System.out
				.println("------------------ \n sent score *didsendscore*!! \n ---------");

		// mMessageText.setText("Upload Successful!"); // set upload progress
		// display
		// isSending = false; // disable transmission-in-progress
		// mTrafficLight.setEnabled(true); // enable traffic light control for
		// replay
	}

	// handles fetching and return of results from not-yet-submitted table and
	// parcels appropriately for transmission
	private ArrayList<NameValuePair> doSetParams() {
		System.out
				.println("------------------ \n setting params!! \n ---------");
		DatabaseHandler db = new DatabaseHandler(this); // create new
														// DatabaseHandler
														// object
		String results = db.getSubmitScores(); // get JSONArray String of
												// yet-to-submit results
		ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(); // create
																			// new
																			// ArrayList
																			// of
																			// NameValuePairs
		params.add(new BasicNameValuePair("results", results)); // put results
																// to params

		System.out.println("------------------ \n params set!! \n ---------");
		return params; // return params
	}
	/**
	 * private void setListener(){ itemListener = new OnItemSelectedListener(){
	 * public void onItemSelected(AdapterView parent, View v, int position, long
	 * id) { iPickedaLevel=position; }
	 * 
	 * @Override public void onNothingSelected(AdapterView parent) { // DO
	 *           NOTHING !!!!!!
	 * 
	 *           } }; levelSelect.setOnItemSelectedListener(itemListener); }
	 */
}
